"""
Authentication Service
认证服务 - JWT认证、令牌管理、权限验证
"""

import uuid
from typing import Optional, Dict, Any, List
from datetime import datetime, timedelta
import jwt
import structlog

from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, and_, or_
from sqlalchemy.orm import selectinload

from ..models.user import User
from ..core.database import DatabaseSession
from ..core.config import settings
from ..core.exceptions import (
    AuthenticationError,
    TokenExpiredError,
    TokenInvalidError,
    PermissionDeniedError,
    UserNotFoundError,
)

# 配置结构化日志
logger = structlog.get_logger(__name__)


class TokenType:
    """令牌类型"""
    ACCESS = "access"
    REFRESH = "refresh"
    RESET_PASSWORD = "reset_password"
    EMAIL_VERIFICATION = "email_verification"


class AuthService:
    """认证服务类"""

    def __init__(self, db_session: DatabaseSession):
        self.db_session = db_session

    def generate_access_token(
        self,
        user_id: uuid.UUID,
        username: str,
        role: str,
        expires_delta: Optional[timedelta] = None
    ) -> str:
        """
        生成访问令牌

        Args:
            user_id: 用户ID
            username: 用户名
            role: 用户角色
            expires_delta: 过期时间间隔

        Returns:
            str: JWT访问令牌
        """
        if expires_delta is None:
            expires_delta = timedelta(hours=settings.security.jwt_expiration_hours)

        expire = datetime.utcnow() + expires_delta
        
        payload = {
            "type": TokenType.ACCESS,
            "sub": str(user_id),
            "username": username,
            "role": role,
            "exp": expire,
            "iat": datetime.utcnow(),
            "jti": str(uuid.uuid4())  # JWT ID，用于令牌撤销
        }

        token = jwt.encode(
            payload,
            settings.security.secret_key,
            algorithm=settings.security.jwt_algorithm
        )

        logger.info("生成访问令牌", user_id=str(user_id), username=username)
        return token

    def generate_refresh_token(
        self,
        user_id: uuid.UUID,
        expires_delta: Optional[timedelta] = None
    ) -> str:
        """
        生成刷新令牌

        Args:
            user_id: 用户ID
            expires_delta: 过期时间间隔

        Returns:
            str: JWT刷新令牌
        """
        if expires_delta is None:
            expires_delta = timedelta(days=30)  # 刷新令牌有效期30天

        expire = datetime.utcnow() + expires_delta
        
        payload = {
            "type": TokenType.REFRESH,
            "sub": str(user_id),
            "exp": expire,
            "iat": datetime.utcnow(),
            "jti": str(uuid.uuid4())
        }

        token = jwt.encode(
            payload,
            settings.security.secret_key,
            algorithm=settings.security.jwt_algorithm
        )

        logger.info("生成刷新令牌", user_id=str(user_id))
        return token

    def generate_reset_password_token(
        self,
        user_id: uuid.UUID,
        expires_delta: Optional[timedelta] = None
    ) -> str:
        """
        生成密码重置令牌

        Args:
            user_id: 用户ID
            expires_delta: 过期时间间隔

        Returns:
            str: JWT密码重置令牌
        """
        if expires_delta is None:
            expires_delta = timedelta(hours=1)  # 密码重置令牌有效期1小时

        expire = datetime.utcnow() + expires_delta
        
        payload = {
            "type": TokenType.RESET_PASSWORD,
            "sub": str(user_id),
            "exp": expire,
            "iat": datetime.utcnow(),
            "jti": str(uuid.uuid4())
        }

        token = jwt.encode(
            payload,
            settings.security.secret_key,
            algorithm=settings.security.jwt_algorithm
        )

        logger.info("生成密码重置令牌", user_id=str(user_id))
        return token

    def generate_email_verification_token(
        self,
        user_id: uuid.UUID,
        email: str,
        expires_delta: Optional[timedelta] = None
    ) -> str:
        """
        生成邮箱验证令牌

        Args:
            user_id: 用户ID
            email: 邮箱地址
            expires_delta: 过期时间间隔

        Returns:
            str: JWT邮箱验证令牌
        """
        if expires_delta is None:
            expires_delta = timedelta(hours=24)  # 邮箱验证令牌有效期24小时

        expire = datetime.utcnow() + expires_delta
        
        payload = {
            "type": TokenType.EMAIL_VERIFICATION,
            "sub": str(user_id),
            "email": email,
            "exp": expire,
            "iat": datetime.utcnow(),
            "jti": str(uuid.uuid4())
        }

        token = jwt.encode(
            payload,
            settings.security.secret_key,
            algorithm=settings.security.jwt_algorithm
        )

        logger.info("生成邮箱验证令牌", user_id=str(user_id), email=email)
        return token

    def verify_token(self, token: str, expected_type: Optional[str] = None) -> Dict[str, Any]:
        """
        验证JWT令牌

        Args:
            token: JWT令牌
            expected_type: 期望的令牌类型

        Returns:
            Dict[str, Any]: 令牌载荷

        Raises:
            TokenExpiredError: 令牌已过期
            TokenInvalidError: 令牌无效
        """
        try:
            payload = jwt.decode(
                token,
                settings.security.secret_key,
                algorithms=[settings.security.jwt_algorithm]
            )

            # 验证令牌类型
            if expected_type and payload.get("type") != expected_type:
                raise TokenInvalidError(f"令牌类型不匹配，期望: {expected_type}, 实际: {payload.get('type')}")

            # 验证必需字段
            required_fields = ["sub", "exp", "iat", "jti"]
            for field in required_fields:
                if field not in payload:
                    raise TokenInvalidError(f"令牌缺少必需字段: {field}")

            logger.debug("令牌验证成功", token_type=payload.get("type"), user_id=payload.get("sub"))
            return payload

        except jwt.ExpiredSignatureError:
            logger.warning("令牌已过期")
            raise TokenExpiredError("令牌已过期")
        except jwt.InvalidTokenError as e:
            logger.warning("令牌无效", error=str(e))
            raise TokenInvalidError(f"令牌无效: {str(e)}")

    def get_user_id_from_token(self, token: str) -> Optional[uuid.UUID]:
        """
        从令牌中获取用户ID

        Args:
            token: JWT令牌

        Returns:
            Optional[uuid.UUID]: 用户ID，验证失败返回None
        """
        try:
            payload = self.verify_token(token)
            user_id_str = payload.get("sub")
            if user_id_str:
                return uuid.UUID(user_id_str)
            return None
        except (TokenExpiredError, TokenInvalidError):
            return None

    async def get_current_user_from_token(self, token: str) -> Optional[User]:
        """
        从令牌中获取当前用户

        Args:
            token: JWT访问令牌

        Returns:
            Optional[User]: 用户对象，验证失败返回None
        """
        try:
            payload = self.verify_token(token, TokenType.ACCESS)
            user_id_str = payload.get("sub")
            if not user_id_str:
                return None

            user_id = uuid.UUID(user_id_str)
            
            async with self.db_session as session:
                result = await session.execute(
                    select(User)
                    .where(User.id == user_id)
                    .options(selectinload(User.settings))
                )
                user = result.scalar_one_or_none()
                
                if user and user.status != "active":
                    logger.warning("用户账户非活跃状态", user_id=user_id_str, status=user.status)
                    return None
                
                return user

        except (TokenExpiredError, TokenInvalidError, UserNotFoundError):
            return None

    def refresh_access_token(self, refresh_token: str) -> Optional[str]:
        """
        刷新访问令牌

        Args:
            refresh_token: JWT刷新令牌

        Returns:
            Optional[str]: 新的访问令牌，验证失败返回None
        """
        try:
            payload = self.verify_token(refresh_token, TokenType.REFRESH)
            user_id = payload.get("sub")
            
            if not user_id:
                return None

            # 生成新的访问令牌
            access_token = self.generate_access_token(
                user_id=uuid.UUID(user_id),
                username=payload.get("username", ""),
                role=payload.get("role", "user")
            )

            logger.info("访问令牌刷新成功", user_id=user_id)
            return access_token

        except (TokenExpiredError, TokenInvalidError):
            logger.warning("刷新令牌无效或已过期")
            return None

    def check_permission(self, user: User, required_permission: str) -> bool:
        """
        检查用户权限

        Args:
            user: 用户对象
            required_permission: 所需权限

        Returns:
            bool: 是否有权限
        """
        if not user:
            return False

        # 管理员拥有所有权限
        if user.is_admin:
            return True

        # 检查用户状态
        if not user.is_active:
            return False

        # 基础权限检查
        basic_permissions = {
            "user:read": True,  # 所有用户都可以读取自己的信息
            "user:update": True,  # 所有用户都可以更新自己的信息
            "content:create": user.can_generate_content(),
            "content:read": True,
            "content:update": True,
            "content:delete": True,
        }

        # 特殊权限检查
        special_permissions = {
            "admin:*": user.is_admin,
            "user:manage": user.is_admin,
            "system:monitor": user.is_admin,
            "content:manage_all": user.is_admin,
        }

        # 检查是否有权限
        if required_permission in basic_permissions:
            return basic_permissions[required_permission]
        elif required_permission in special_permissions:
            return special_permissions[required_permission]
        else:
            logger.warning("未知权限检查", permission=required_permission, user_id=user.id)
            return False

    def require_permission(self, user: User, required_permission: str) -> None:
        """
        要求用户权限，无权限则抛出异常

        Args:
            user: 用户对象
            required_permission: 所需权限

        Raises:
            PermissionDeniedError: 权限不足
        """
        if not self.check_permission(user, required_permission):
            logger.warning("权限检查失败", 
                         user_id=user.id if user else None,
                         permission=required_permission)
            raise PermissionDeniedError(f"权限不足: {required_permission}")

    def check_role(self, user: User, required_roles: List[str]) -> bool:
        """
        检查用户角色

        Args:
            user: 用户对象
            required_roles: 所需角色列表

        Returns:
            bool: 是否有角色
        """
        if not user:
            return False

        return user.role in required_roles

    def require_role(self, user: User, required_roles: List[str]) -> None:
        """
        要求用户角色，无角色则抛出异常

        Args:
            user: 用户对象
            required_roles: 所需角色列表

        Raises:
            PermissionDeniedError: 角色不符
        """
        if not self.check_role(user, required_roles):
            logger.warning("角色检查失败",
                         user_id=user.id if user else None,
                         user_role=user.role if user else None,
                         required_roles=required_roles)
            raise PermissionDeniedError(f"需要角色: {', '.join(required_roles)}")

    async def revoke_token(self, token: str) -> bool:
        """
        撤销令牌（实现令牌黑名单）

        Args:
            token: 要撤销的令牌

        Returns:
            bool: 是否成功撤销
        """
        try:
            payload = self.verify_token(token)
            jti = payload.get("jti")
            
            if not jti:
                return False

            # TODO: 实现令牌黑名单存储
            # 这里可以实现将令牌JTI存储到数据库或Redis中
            logger.info("令牌撤销请求", jti=jti, user_id=payload.get("sub"))
            return True

        except (TokenExpiredError, TokenInvalidError):
            logger.warning("尝试撤销无效令牌")
            return False

    async def cleanup_expired_tokens(self) -> int:
        """
        清理过期的令牌（维护任务）

        Returns:
            int: 清理的令牌数量
        """
        # TODO: 实现令牌黑名单清理
        # 这里可以实现清理数据库或Redis中的过期令牌
        logger.info("清理过期令牌任务完成")
        return 0

    def create_auth_response(self, user: User) -> Dict[str, Any]:
        """
        创建认证响应

        Args:
            user: 用户对象

        Returns:
            Dict[str, Any]: 认证响应数据
        """
        access_token = self.generate_access_token(
            user_id=user.id,
            username=user.username,
            role=user.role
        )

        refresh_token = self.generate_refresh_token(user_id=user.id)

        return {
            "access_token": access_token,
            "refresh_token": refresh_token,
            "token_type": "bearer",
            "expires_in": settings.security.jwt_expiration_hours * 3600,
            "user": {
                "id": str(user.id),
                "username": user.username,
                "email": user.email,
                "full_name": user.full_name,
                "role": user.role,
                "avatar_url": user.avatar_url,
                "email_verified": user.email_verified,
                "created_at": user.created_at.isoformat() if user.created_at else None,
            }
        }

    def validate_api_key(self, api_key: str) -> Optional[Dict[str, Any]]:
        """
        验证API密钥

        Args:
            api_key: API密钥

        Returns:
            Optional[Dict[str, Any]]: 验证结果，失败返回None
        """
        # TODO: 实现API密钥验证
        # 这里可以实现API密钥的数据库存储和验证
        logger.debug("API密钥验证", api_key=api_key[:10] + "..." if api_key else None)
        return None

    async def get_user_permissions(self, user: User) -> List[str]:
        """
        获取用户权限列表

        Args:
            user: 用户对象

        Returns:
            List[str]: 权限列表
        """
        permissions = []

        # 基础权限
        permissions.extend([
            "user:read",
            "user:update",
            "content:read",
        ])

        # 内容创建权限
        if user.can_generate_content():
            permissions.extend([
                "content:create",
                "content:update",
                "content:delete",
            ])

        # 管理员权限
        if user.is_admin:
            permissions.extend([
                "admin:*",
                "user:manage",
                "system:monitor",
                "content:manage_all",
                "task:manage_all",
                "folder:manage_all",
            ])

        return permissions